#include "stdafx.h"
#include "Any.h"
#include <sstream>
#include <boost/algorithm/string/replace.hpp>
#include <boost/lexical_cast.hpp>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

namespace serika
{
	using ut::Time;

	namespace db
	{
		const ut::Char TYPE_NULL = 0;
		const ut::Char TYPE_INTEGER = 1;
		const ut::Char TYPE_DOUBLE = 2;
		const ut::Char TYPE_STRING = 3;
		const ut::Char TYPE_CHAR = 4;
		const ut::Char TYPE_TIME = 5;
	
		ut::String Any::ToString() const
		{
			ut::Sstream ss;
			ss << *this;
			return ss.str();
		}
	
		ut::String Any::ToPersistence() const
		{
			ut::Sstream ss;
			this->ToPersistence(ss);
			return ss.str();
		}
	
		ut::Ostream& Any::ToPersistence(ut::Ostream& os) const
		{
			int iv = 0;
			double dv = 0;
			ut::String sv = _T("");
			Time tv;
			ut::Char cv = '\0';
			if (this->IsEmpty())
			{
				os << _T("N:(NULL)");
			}
			else if (this->TryGet(iv))
			{
				os << _T("I:") << iv;
			}
			else if (this->TryGet(dv))
			{
				os << _T("D:") << dv;
			}
			else if (this->TryGet(sv))
			{
				os << _T("S") << sv.length() << ":" << sv;
			}
			else if (this->TryGet(cv))
			{
				os << _T("C:") << cv;
			}
			else if (this->TryGet(tv))
			{
				os << _T("T:") << tv.ToString();
			}
			else 
			{
				throw std::bad_typeid();
			}
			return os;
		}
	
		bool Any::FromPersistence(const ut::String& text, size_t& start)
		{
			switch (text[start])
			{
			case 'I':
				{
					start += 2;
					size_t end = text.find_first_of(_T(",}"), start);
					ut::String t = (end == ut::String::npos) ? text.substr(start) : text.substr(start, end - start);
					start = (end == ut::String::npos) ? ut::String::npos : end + 1;
					*this = boost::lexical_cast<int>(t);
					//				this->operator =(atoi(t.c_str()));
				}
				break;
	
			case 'D':
				{
					start += 2;
					size_t end = text.find_first_of(_T(",}"), start);
					ut::String t = (end == ut::String::npos) ? text.substr(start) : text.substr(start, end - start);
					start = (end == ut::String::npos) ? ut::String::npos : end + 1;
					*this = boost::lexical_cast<double>(t);
					//				this->operator =(atof(t.c_str()));
				}
				break;
	
			case 'N':
				this->Clear();
				start += 8;
				break;
	
			case 'S':
				{
					start += 1;
					size_t end = text.find_first_of(':', start);
					ut::String t = text.substr(start, end - start);
					//int length = atoi(t.c_str());
					int length = boost::lexical_cast<int>(t);
					start = end + 1;
					t = text.substr(start, length);
					start += length + 1;
					*this = t;
					//this->operator = (t);
				}
				break;
	
			case 'C':
				{
					start += 2;
					this->operator =<ut::Char>(text[start]);
					start += 2;
				}
				break;
	
			case 'T':
				{
					start += 2;
					Time time = Time::FromString(text.substr(start, 19).c_str());
					start += 20;
					*this = time;
				}
				break;
	
			default:
				return false;
			}
			return true;
		}
		
		bool Any::FromPersistence(ut::Istream& is)
		{
			ut::Char ch = is.get();
			const size_t length = 1024;
			ut::Char buf[length] = {0};
			switch (ch)
			{
			case 'I':
				{
					is.get();		// skip the colon
					this->ReadValueFromStream(is, buf, length);
					//this->operator = (atoi(buf));
					*this = boost::lexical_cast<int>(buf);
				}
				break;
	
			case 'D':
				{
					is.get();		// skip the colon
					this->ReadValueFromStream(is, buf, length);
					*this = boost::lexical_cast<double>(buf);
					//this->operator = (atof(buf));
				}
				break;
	
			case 'N':
				this->Clear();
				is.get(buf, 9);
				break;
	
			case 'S':
				{
					is.get(buf, length, ':');
#ifdef _UNICODE
					size_t l = _wtoi(buf);
#else
					size_t l = atoi(buf);
#endif
					ut::Char* t = l > length - 1 ? new ut::Char[l] : buf;
					is.get();		// skip the colon
					if (l > 0)
					{
						is.get(t, l + 1);
						ut::String s(t, is.gcount());
						this->operator = (s);
					}
					else
					{
						ut::String s(_T(""));
						this->operator = (s);
					}
					if (l > length)
					{
						delete[] t;
					}
				}
				break;
	
			case 'C':
				{
					is.get();	// skip the colon
					ch = is.get();
					this->operator = (ch);
				}
				break;
	
			case 'T':
				{
					size_t count = 0;
					is.get();	// skip the colon
					is.get(buf, 20);
					buf[is.gcount()] = 0;
					if (is.eof())
					{
						return false;
					}
					Time time = Time::FromString(buf);
					if (time == Time::INVALID)
					{
						return false;
					}
					*this = time;
				}
				break;
	
			default:
				return false;
			}
			return true;
		}	
	
		size_t Any::ToBinary(ut::Char* buffer, size_t bufferLen) const
		{
			int iv = 0;
			double dv = 0;
			ut::String sv = _T("");
			Time tv;
			ut::Char cv = '\0';
			if (this->IsEmpty())
			{
				if (bufferLen >= sizeof(char))
				{
					*buffer = TYPE_NULL;
					return sizeof(char);
				}
				return -1;
			}
			else if (this->TryGet(iv))
			{
				if (bufferLen >= sizeof(int) + sizeof(char))
				{
					*buffer++ = TYPE_INTEGER;
					memcpy(buffer, &iv, sizeof(int));
					return sizeof(int) + sizeof(char);
				}
				return -1;
			}
			else if (this->TryGet(dv))
			{
				if (bufferLen >= sizeof(double) + sizeof(char))
				{
					*buffer++ = TYPE_DOUBLE;
					memcpy(buffer, &dv, sizeof(double));
					return sizeof(double) + sizeof(char);
				}
				return -1;
			}
			else if (this->type() == typeid(ut::String))
			{
				this->TryGet(sv);
				size_t size = sv.size();
				if (size > 0xFFFF)
				{
					return -1;
				}
				unsigned short ssize = (unsigned short)size;
				if (bufferLen >= size + sizeof(char) + sizeof(short))
				{
					*buffer++ = TYPE_STRING;
					memcpy(buffer, &ssize, sizeof(short));
					buffer += sizeof(short);
					memcpy(buffer, sv.c_str(), size);
					return size + sizeof(char) + sizeof(short);
				}
				return -1;
			}
			else if (this->TryGet(cv))
			{
				if (bufferLen >= sizeof(char) + sizeof(char))
				{
					*buffer++ = TYPE_CHAR;
					*buffer++ = cv;
					return sizeof(char) + sizeof(char);
				}
				return -1;
			}
			else if (this->type() == typeid(Time))
			{
				this->TryGet(tv);
				if (bufferLen >= sizeof(Time) + sizeof(char))
				{
					*buffer++ = TYPE_TIME;
					memcpy(buffer, &tv, sizeof(Time));
					return sizeof(Time) + sizeof(char);
				}
				return -1;
			}
			else 
			{
				return -1;
			}
		}
	
		size_t Any::FromBinary(const ut::Char* data, size_t dataLen)
		{
			if (dataLen < sizeof(char))
			{
				return -1;
			}
			ut::Char ch = *data;
			size_t totalLen = sizeof(ut::Char);
			switch (ch)
			{
			case TYPE_NULL:
				{
					this->Clear();
					return totalLen;
				}
	
			case TYPE_INTEGER:
				{
					if (dataLen - totalLen < sizeof(int))
					{
						return -1;
					}
					int v = 0;
					memcpy(&v, data + totalLen, sizeof(int));
					this->operator =(v);
					return totalLen + sizeof(int);
				}
	
			case TYPE_DOUBLE:
				{
					if (dataLen - totalLen < sizeof(double))
					{
						return -1;
					}
					double v = 0;
					memcpy(&v, data + totalLen, sizeof(double));
					this->operator =(v);
					return totalLen + sizeof(double);
				}
	
			case TYPE_STRING:
				{
					if (dataLen - totalLen < sizeof(short))
					{
						return -1;
					}
					unsigned short ssize = 0;
					memcpy(&ssize, data + totalLen, sizeof(short));
					totalLen += sizeof(short);
					if (dataLen - totalLen < ssize)
					{
						return -1;
					}
					if (ssize > 0)
					{
						ut::String s(data + totalLen, ssize);
						this->operator =(s);
					}
					else
					{
						this->operator =(ut::String(_T("")));
					}
					return totalLen + ssize;
				}
	
			case TYPE_CHAR:
				{
					if (dataLen - totalLen < sizeof(char))
					{
						return -1;
					}
					this->operator =(*(data + totalLen));
					return totalLen + sizeof(char);
				}
	
			case TYPE_TIME:
				{
					if (dataLen - totalLen < sizeof(Time))
					{
						return -1;
					}
					Time v;
					memcpy(&v, data + totalLen, sizeof(Time));
					this->operator =(v);
					return totalLen + sizeof(Time);
				}
	
			default:
				return -1;
			}
		}
	
		size_t Any::ReadValueFromStream(ut::Istream& is, ut::Char* buf, size_t /*length*/)
		{
			ut::Char ch;
			size_t ret = 0;
			while(true)
			{
				ch = is.get();
				if (ch == ',' || ch == '}')
				{
					is.putback(ch);
					return ret;
				}
				if (is.eof())
				{
					return ret;
				}
				buf[ret++] = ch;
			}
			return ret;
		}
	}
	
	std::ostream& operator << (std::ostream& s, const db::Any& v)
	{
		int iv = 0;
		double dv = 0;
		float fv = 0;
		ut::String sv = _T("");
		ut::Char* csv = 0;
		Time tv;
		ut::Char cv = '\0';
		if (v.IsEmpty())
		{
			s << "(NULL)";
		}
		else if (v.TryGet(iv))
		{
			s << iv;
		}
		else if (v.TryGet(dv))
		{
			s << dv;
		}
		else if (v.TryGet(fv))
		{
			s << fv;
		}
		else if (v.TryGet(sv))
		{
			s << TO_ASTRING(sv);
		}
		else if (v.TryGet(csv))
		{
			s << csv;
		}
		else if (v.TryGet(cv))
		{
			s << cv;
		}
		else if (v.TryGet(tv))
		{
			s << TO_ASTRING(tv.ToString());
		}
		else 
		{
			throw std::bad_typeid();
		}
		return s;
	}

	std::wostream& operator << (std::wostream& s, const db::Any& v)
	{
		int iv = 0;
		double dv = 0;
		float fv = 0;
		ut::String sv = _T("");
		ut::Char* csv = 0;
		Time tv;
		ut::Char cv = '\0';
		if (v.IsEmpty())
		{
			s << L"(NULL)";
		}
		else if (v.TryGet(iv))
		{
			s << iv;
		}
		else if (v.TryGet(dv))
		{
			s << dv;
		}
		else if (v.TryGet(fv))
		{
			s << fv;
		}
		else if (v.TryGet(sv))
		{
			s << TO_WSTRING(sv);
		}
		else if (v.TryGet(csv))
		{
			s << csv;
		}
		else if (v.TryGet(cv))
		{
			s << cv;
		}
		else if (v.TryGet(tv))
		{
			s << TO_WSTRING(tv.ToString());
		}
		else 
		{
			throw std::bad_typeid();
		}
		return s;
	}
}
